package com

import com.entity.PackPath

object Crc32 {

    private val crcTable: Array<Int> by lazy {
        val table = Array(256) { 0 }
        var crc: Int
        table.forEachIndexed { index, _ ->
            crc = index
            for (i in 0..<8) {
                crc = if (crc and 1 == 1) crc ushr 1 xor -0x12477ce0 else crc ushr 1
            }
            table[index] = crc ushr 0
        }
        table
    }

    /**
     * 将字符串生成crc值
     */
    fun crc32(str: String): Int {
        val utf8CharCodes = utf8encode(str)
        var crc = -1
        for (element in utf8CharCodes) {
            val y = crc xor element and 0xFF
            crc = crc ushr 8 xor crcTable[y]
        }
        return crc xor -1 ushr 0
    }

    private fun utf8encode(str: String): List<Int> {
        val utf8CharCodes = mutableListOf<Int>()
        for (i in str) {
            val c = i.code
            if (c < 128) {
                utf8CharCodes.add(c)
            } else if (c < 2048) {
                utf8CharCodes.add(c shr 6 or 192)
                utf8CharCodes.add(c and 63 or 128)
            } else {
                utf8CharCodes.add(c shr 12 or 224)
                utf8CharCodes.add(c shr 6 and 63 or 128)
                utf8CharCodes.add(c and 63 or 128)
            }
        }
        return utf8CharCodes
    }


    fun crc32Hex(path: PackPath): String {
        return crc32Hex(path.readBytes())
    }

    @OptIn(ExperimentalStdlibApi::class)
    fun crc32Hex(buff: ByteArray): String {
        val pathcrcv = crc32(buff)
        return Integer.toHexString(pathcrcv.toInt())
    }

    /**
     * //        println(Integer.toHexString(240089644))
     * //        val re = BigInteger("e4f7a2c", 16).intValue()
     * //        println(re)
     */

    fun crc32(path: PackPath): UInt {
        return crc32(path.readBytes())
    }

    /**
     * 将二进制数据生成crc值
     */
    fun crc32(buff: ByteArray): UInt {
        val len = buff.size
        // 计算简单校验
        val checkbuff = ByteArray(8) { 0 }
        val clen = len - len % 8  // 最后剩下的算法不同，所以要分开

        for (i in 0 until clen) {
            checkbuff[i % 8] = (checkbuff[i % 8].toInt() xor buff[i].toInt()).toByte()
        }

        for (i in clen until len) {
            checkbuff[0] = (checkbuff[0].toInt() xor buff[i].toInt()).toByte()
        }

        val checksum = ByteArray(4) { 0 }
        checksum[0] = (checkbuff[0].toInt() xor checkbuff[4].toInt()).toByte()
        checksum[1] = (checkbuff[1].toInt() xor checkbuff[5].toInt()).toByte()
        checksum[2] = (checkbuff[2].toInt() xor checkbuff[6].toInt()).toByte()
        checksum[3] = (checkbuff[3].toInt() xor checkbuff[7].toInt()).toByte()
        // 以小端序处理
        val crc = (checksum[0].toULong() and 0xFFu) or
                ((checksum[1].toULong() and 0xFFu) shl 8) or
                ((checksum[2].toULong() and 0xFFu) shl 16) or
                ((checksum[3].toULong() and 0xFFu) shl 24)

        // 由于这个算法太简单，可能会产生出0来，而0会被检验函数认为错误，所以要处理一下
        return if (crc == 0UL) 1u else crc.toUInt()
    }

}